#include <atomic>
#include <cstring>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
#include <memory>
#include <ctime>
#include <unordered_map>

// ============== Windows ==============
#if defined(_WIN32) || defined(_WIN64)
#include <cstdint>
#include <ws2tcpip.h>
#include <winsock2.h>
#include "ws2ipdef.h"
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#define close(sock) closesocket(sock)
typedef SOCKET socket_t;
#define INVALID_SOCKET_VALUE INVALID_SOCKET
#define FD_SET(n, p) ((p)->fds_bits[(n) / BITSPERWORD] |= ((fd_mask) 1 << ((n) % BITSPERWORD)))
#define FD_CLR(n, p) ((p)->fds_bits[(n) / BITSPERWORD] &= ~((fd_mask) 1 << ((n) % BITSPERWORD)))
#define FD_ISSET(n, p) ((p)->fds_bits[(n) / BITSPERWORD] & ((fd_mask) 1 << ((n) % BITSPERWORD)))
using ushort = unsigned short;
// =========  Unix/Linux/Macos  =========
#else
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <ifaddrs.h> //ipv4
#include <netdb.h>   //ipv4
#include <sys/select.h>
#define INVALID_SOCKET_VALUE -1
typedef int             socket_t;
typedef struct sockaddr SOCKADDR;
#endif

#include <algorithm>

#define BUFFER_SIZE 1024
#define SELECT_TIMEOUT 1

using namespace std;

// 动态创建多个Socket，并可通过端口号关闭Socket
class DynamicSocketsManager {
private:
	unordered_map<int, int> fd2port;
	unordered_map<int, int> port2fd;
	mutex                   maps_mutex;
	atomic<bool>            running{false};
	thread                  work_thread;

	void worker() {
		while (running) {
			fd_set read_fds;
			FD_ZERO(&read_fds);
			int max_id = 0;
			{
				lock_guard<mutex> lock(maps_mutex);
				for (const auto &pair: fd2port) {
					int fd = pair.first;
					FD_SET(fd, &read_fds);
					if (fd > max_id) max_id = fd;
				}
			}
			struct timeval tv{.tv_sec = SELECT_TIMEOUT, .tv_usec = 0};
			int            activity = select(max_id + 1, &read_fds, nullptr, nullptr, &tv);
			if (activity < 0 && errno != EINTR) continue;
			vector<int> ready_fds;
			{
				lock_guard<mutex> lock(maps_mutex);
				for (const auto &pair: fd2port) {
					if (FD_ISSET(pair.first, &read_fds)) {
						ready_fds.push_back(pair.first);
					};
				}
			}
			for (int fd: ready_fds) {
				handle_data(fd);
			}
		}
	}

	void handle_data(int sockfd) {
		char               buffer[BUFFER_SIZE];
		struct sockaddr_in client_addr{};
		socklen_t          addr_len = sizeof(client_addr);
		ssize_t            recv_len = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *) &client_addr, &addr_len);
		if (recv_len < 0) {
			cerr << "Error receiveing data on fd:" << sockfd << std::endl;
			destroyByFd(sockfd);
		} else {
			std::cout << "\nReceived " << recv_len << "bytes on port:" << fd2port[sockfd] << std::endl;
		}
	}

	void destroyByFd(int sockfd) {
		lock_guard<mutex> lock(maps_mutex);
		if (fd2port.count(sockfd)) {
			int port = fd2port[sockfd];
			port2fd.erase(port);
			fd2port.erase(sockfd);
			close(sockfd);
			std::cout << "closed on port: " << port << std::endl;
		}
	}

public:
	DynamicSocketsManager() {
		work_thread = std::thread(&DynamicSocketsManager::worker, this);
		running     = true;
	}
	~DynamicSocketsManager() {
		running = false;
		if (work_thread.joinable()) {
			work_thread.join();
		}

		lock_guard<mutex> lock(maps_mutex);
		for (const auto &pair: fd2port) {
			close(pair.first);
		}
		fd2port.clear();
		port2fd.clear();
	}

	int createSocket() {
		int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		if (sockfd < 1) return -1;

		struct sockaddr_in server_addr{};
		memset(&server_addr, 0, sizeof(server_addr));
		server_addr.sin_family      = AF_INET;
		server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
		server_addr.sin_port        = htons(0);
		if (::bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
			close(sockfd);
			return -1;
		}

		socklen_t len = sizeof(server_addr);
		getsockname(sockfd, (struct sockaddr *) &server_addr, &len);
		int port = ntohs(server_addr.sin_port);

		lock_guard<mutex> lock(maps_mutex);
		fd2port[sockfd] = port;
		port2fd[port]   = sockfd;
		return port;
	}

	void destroySocket(int port) {
		lock_guard<mutex> lock(maps_mutex);
		if (port2fd.count(port)) {
			int sockfd = port2fd[port];
			port2fd.erase(port);
			fd2port.erase(sockfd);
			std::cout << "closed on port: " << port << std::endl;
			close(sockfd);
		}
	}

	DynamicSocketsManager(const DynamicSocketsManager &)            = delete;
	DynamicSocketsManager &operator=(const DynamicSocketsManager &) = delete;
};

// Linux：   g++ -std=c++11 -pthread -o dynamic_sockets.exe dynamic_sockets.cpp
// Windows： g++ -std=c++11 -pthread -o dynamic_sockets.exe dynamic_sockets.cpp -lws2_32
int main() {
	DynamicSocketsManager manager;
	try {
		int port1 = manager.createSocket();
		int port2 = manager.createSocket();

		std::cout << "created on ports: " << port1 << endl;
		std::cout << "created on ports: " << port2 << endl;

		std::cout << "\npress any key to close" << endl;
		getchar();

		manager.destroySocket(port1);
		manager.destroySocket(port2);
	} catch (const std::exception &e) {
		std::cerr << e.what() << '\n';
	}
	return 0;
}
